﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Citi.HeadCount.Domain;
using Citi.HeadCount.Domain.Repository;
using Citi.HeadCount.WebUI.Models;

namespace Citi.HeadCount.WebUI.Controllers
{
    public class RuleResultController : Controller
    {
        private RuleResultRepository RuleResultRepository { get; set; }

        public RuleResultController()
        {
            RuleResultRepository = new RuleResultRepository();
        }

        public ActionResult Index()
        {
            var models = RuleResultViewModel.CreateViewModel(RuleResultRepository.GetActiveRulesButException());
            return View(models);
        }

        public ActionResult Delete(int id)
        {
            RuleResultRepository.Inative(id);
            return RedirectToAction("Index");
        }

        public ActionResult CreateException()
        {
            return PartialView("Create");
        }

        [HttpPost]
        public ActionResult Save(RuleResultViewModel ruleResult, List<string> rules)
        {
            SaveRuleResult(ruleResult);
            SaveRules(ruleResult.Id, rules);

            return RedirectToAction("Index");
        }

        private void SaveRules(int ruleResultId, List<string> rules)
        {
            var rulesModel = rules.Select(x => CreateModelFromString(x, ruleResultId));

            var rulesDomain = rulesModel.Where(x => x.Id == null).Select(RuleViewModel.CreateDomain);
            var ruleRepository = new RuleRepository();
            ruleRepository.Save(rulesDomain);

            rulesDomain = rulesModel.Where(x => x.Id != null).Select(x =>
            {
                var rule = ruleRepository.GetById(x.Id);

                rule.LastUpdate = DateTime.Now;
                rule.Level = x.Level;
                rule.Value = x.Value;

                return rule;
            });

            ruleRepository.Save(rulesDomain);
        }

        private void SaveRuleResult(RuleResultViewModel ruleResult)
        {
            RuleResult ruleResultDomain;

            if (ruleResult.Id == 0)
                ruleResultDomain = new RuleResult { LastUpdate = DateTime.Today, ReferenceDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1) , Active = true, Name = ruleResult.Name };
            else
            {
                ruleResultDomain = RuleResultRepository.GetById(ruleResult.Id);

                if (!new GOCRepository().GetAll().Any(x => x.RuleResultId == ruleResult.Id))
                {
                    ruleResultDomain.Name = ruleResult.Name;
                    ruleResultDomain.LastUpdate = DateTime.Now;
                }
                else
                {
                    ruleResultDomain.Active = false;
                    RuleResultRepository.Save(ruleResultDomain);

                    ruleResultDomain = new RuleResult { LastUpdate = DateTime.Today, ReferenceDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1), Active = true, Name = ruleResult.Name };
                }
            }

            RuleResultRepository.Save(ruleResultDomain);
        }

        private RuleViewModel CreateModelFromString(string ruleString, int ruleResultId)
        {
            var splited = ruleString.Split(',');

            return new RuleViewModel
            {
                Id = int.Parse(splited[0]),
                Level = int.Parse(splited[2]),
                Type = int.Parse(splited[4]),
                RuleResultId = ruleResultId,
                TypeDescription = splited[1],
                Value = splited[3]
            };
        }

        public ActionResult Create(int id = 0)
        {
            if (id == 0)
                return View();

            var domain = new RepositoryBase<RuleResult>().GetById(id);
            return View(RuleResultViewModel.CreateViewModel(domain));
        }

        public ActionResult ApplyRules(string referenceDate)
        {
            var repository = new GOCRepository();

            //var referenceDate = new DateTime(2013, 04, 01);

            var gocs = repository.GetGOCsByReferenceDate(DateTime.Parse(referenceDate));
            var rules = new RuleResultRepository().GetActiveRules();

            ApplyRules(repository, gocs, rules);
            return View();
        }

        public ActionResult ApplyException(RuleResult ruleResult)
        {
            var repository = new GOCRepository();

            var gocs = repository.GetGOCsRuleException();
            ApplyRules(repository, gocs, new List<RuleResult> { ruleResult });
            return View();
        }

        private static void ApplyRules(GOCRepository repository, List<GOC> gocs, IEnumerable<RuleResult> rules)
        {
            foreach (var ruleResult in rules.OrderByDescending(x => x.Rules.Count(y => y.Type == RuleType.ManagedSegment))
                .ThenBy(x => x.Rules.Count(y => y.Type == RuleType.Function)))
            {
                var gocsSelecionados = gocs.AsEnumerable().ToList();

                foreach (var rule in ruleResult.Rules.OrderByDescending(x => (int)x.Type).ThenBy(x => x.Level))
                {
                    switch (rule.Type)
                    {
                        case RuleType.OU:
                            gocsSelecionados = gocsSelecionados.Where(x => x.FRSOu.Code == rule.Value).ToList();
                            break;
                        case RuleType.LVID:
                            gocsSelecionados = gocsSelecionados.Where(x => x.Level.Code == rule.Value).ToList();
                            break;
                        case RuleType.ManagedSegment:
                            gocsSelecionados = GetGocsByManagedSegment(gocsSelecionados, rule);
                            break;
                        case RuleType.Function:
                            gocsSelecionados = GetGocsByFunction(gocsSelecionados, rule);
                            break;
                    }
                }

                if (!gocsSelecionados.Any() || gocsSelecionados.All(x => x.RuleResultId == ruleResult.Id)) continue;

                foreach (var goc in gocsSelecionados.Where(x => x.RuleResultId != ruleResult.Id).ToList())
                {
                    var selectedGoc = gocs.First(x => x.Id == goc.Id);
                    //gocs.RemoveAll(x => x.Id == goc.Id);
                    selectedGoc.RuleResultId = ruleResult.Id;
                }
            }

            repository.Save(gocs);
        }

        private static List<GOC> GetGocsByManagedSegment(List<GOC> gocsSelecionados, Rule rule)
        {
            var gocsManagedSegment = gocsSelecionados.AsEnumerable();
            gocsSelecionados = new List<GOC>();

            foreach (var goc in gocsManagedSegment)
            {
                var managedSegment = goc.ManagedSegment;
                var itemNotFound = false;

                while (managedSegment.TreeLevel != rule.Level)
                {
                    if (managedSegment.Parent == null)
                    {
                        itemNotFound = true;
                        break;
                    }
                    managedSegment = managedSegment.Parent;
                }

                if (!itemNotFound && managedSegment.Description == rule.Value)
                    gocsSelecionados.Add(goc);
            };
            return gocsSelecionados;
        }

        private static List<GOC> GetGocsByFunction(List<GOC> gocsSelecionados, Rule rule)
        {
            var gocsFunction = gocsSelecionados.AsEnumerable();
            gocsSelecionados = new List<GOC>();

            foreach (var goc in gocsFunction)
            {
                var function = goc.Function;
                var itemNotFound = false;

                while (function.TreeLevel != rule.Level)
                {
                    if (function.Parent == null)
                    {
                        itemNotFound = true;
                        break;
                    }
                    function = function.Parent;
                }

                if (!itemNotFound && function.Description == rule.Value)
                    gocsSelecionados.Add(goc);
            };
            return gocsSelecionados;
        }
    }
}
